﻿/*-------------------------------------------------------------------------
* 命名空间: XrayDirection.Common/SingleInstanceManager
* 类       名: SingleInstanceManager
* 功      能:  TD
* 时      间:  2023/12/25 17:36:31
* 版      本:  V1.0
* 作      者:  Fioman
* 格      言:  Talk is cheap,show me the code ^_^
*-------------------------------------------------------------------------*/
using SiliconSelect.Common;
using System;
using System.Threading;
using System.Threading.Tasks;
using XrayDirection.HelperManager;

namespace XrayDirection.Common
{
    /// <summary>
    /// 应用程序的单例管理类,如果用户尝试穹顶已经运行的程序的另外一个实例,它将会把已经运行的应用程序实例
    /// 带到前台,而不是启动新的应用程序实例.
    /// </summary>
    public class SingleInstanceManager
    {
        // 用于同步线程的访问,防止同时启动多个应用程序的实例,我们可以称之为互斥体对象
        private static Mutex? _mutex;
        private static Task? _task;

        // 传感Mutex的标识,用来唯一标识这个Mutex,只要创建的时候使用了这个Id,就代表是同一个Mutex
        private const string _mutexId = "XrayDirection-mutex-2023-12-25-Fioman-Start";

        // 用于创建EventHandle的唯一标识字符串,只要创建了EvetnWaitHandle的时候使用了这个字符串相等
        // 就代表是同一个EventHandle
        private const string _eventHandleId = "XrayDirection-event-2023-12-25-Fioman-Start";

        public static void SingleInstanceCheck()
        {
            /*
             * 创建一个线程互斥体对象,各个参数的解释:
             * 1) true: 调用线程是否用于创建的互斥体的所有权,true标识创建Mutex的线程直接拥有Mutex的所有权.
             * 如果设置为true,表示创建Mutex的线程将立即获取互斥体的所有权,其他的线程在这个线程释放互斥体
             * 之前,都不能获取这个互斥体,也不能访问这个互斥体保护的资源.
             * 当说一个线程拥有一个互斥体的所有权的时候,就是说这个线程可以对互斥体进行操作:
             * 如等待互斥体,释放互斥体等,其他的线程只能等待获取互斥体的所有权,不能进行其他的操作.
             * 
             * 2) _mutextId: 一个字符串,用于为Mutex实例创建一个系统范围内的名称,这个名称在全局范围内是唯一的,
             * 这样其他的线程或者是进程可以通过这个名称找到并使用这个MutexId
             * 
             * 3) out bool createdNew
             * 输出参数,当这个函数返回时,如果当前的互斥体已经存在,就证明程序已经开启了,createdNew返回False,
             * 如果说CreatedNew为True,表示还没有开启程序,然后打开程序即可
             * 
             */
            _mutex = new Mutex(true, _mutexId, out bool createdNew);
            if (!createdNew)
            {
                // 程序已经启动了,在这里就是激活已经启动的程序
                LogHelper.WriteLog("程序已经启动,激活已经启动的程序!");
                // 如果程序已经开启,创建一个EventWaitHandle(等待事件句柄,通知事件发生)
                var handle = CreateWaitHanle();
                handle.Set(); // 设定事件等待句柄有有信号状态,可以理解为发送了一个信号
                // 退出当前的应用
                Environment.Exit(0);
            }
            else
            {
                LogHelper.WriteLog("程序启动,启动新程序");
            }
        }

        private static EventWaitHandle CreateWaitHanle()
        {
            /*
             * 各个参数的解释:
             * 1) false
             * EventWaitHandle的初始状态,如果为true表示有信号状态,就是试图等待这个信号的地方会立即执行,不会阻塞.
             * 如果为false,表示的是无信号状态,试图等待这个信号的会阻塞等待,直到等待下一个信号为止.
             * 2) EventResetMode.AutoReset
             * 就是自动复位模式,当一个线程收到信号后,这个信号会自动复位为无信号,如果不设置成这样,然后不手动复位这个信号
             * ,那么所有的等待这个信号的地方都会收到这个信号,并且这个信号会一直存在.
             * 3) EventWaitHandleId 全局唯一标识名称,所有的线程都是通过这个Id来访问同一个EventWaitHandle实例的
             */
            return new EventWaitHandle(false, EventResetMode.AutoReset, _eventHandleId);
        }
    }
}
